﻿// Open Source: MIT License (http://www.opensource.org/licenses/mit-license.php)
// Project: YoProgramo.NET (http://code.google.com/p/yoprogramo/)
// Autor: Fabian Figueredo (info@fabianfigueredo.com.ar)
// Blog: blog.yoprogramo.net

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YoProgramo.Domain.Core.Interfaces
{
    public abstract class EntityBase<IdT> {
        /// <summary>
        /// ID may be of type string, int, custom type, etc.
        /// Setter is protected to allow unit tests to set this property via reflection and to allow 
        /// domain objects more flexibility in setting this for those objects with assigned IDs.
        /// </summary>
        public IdT Id {
            get { return id; }
            protected set { id = value; }
        }

        public virtual bool Equals(object obj) {
            EntityBase<IdT> compareTo = obj as EntityBase<IdT>;

            return (compareTo != null) &&
                   (HasSameNonDefaultIdAs(compareTo) ||
                    // Since the IDs aren't the same, either of them must be transient to 
                    // compare business value signatures
                    (((IsTransient()) || compareTo.IsTransient()) &&
                     HasSameBusinessSignatureAs(compareTo)));
        }

        /// <summary>
        /// Transient objects are not associated with an item already in storage.  For instance,
        /// a <see cref="Customer" /> is transient if its ID is 0.
        /// </summary>
        public virtual bool IsTransient() {
            return Id == null || Id.Equals(default(IdT));
        }

        /// <summary>
        /// Must be provided to properly compare two objects
        /// </summary>
        public abstract override int GetHashCode();

        private bool HasSameBusinessSignatureAs(EntityBase<IdT> compareTo) {
            Check.Require(compareTo != null, "compareTo may not be null");

            return GetHashCode().Equals(compareTo.GetHashCode());
        }

        /// <summary>
        /// Returns true if self and the provided persistent object have the same ID values 
        /// and the IDs are not of the default ID value
        /// </summary>
        private bool HasSameNonDefaultIdAs(EntityBase<IdT> compareTo) {
            Check.Require(compareTo != null, "compareTo may not be null");

            return (Id != null && !Id.Equals(default(IdT))) &&
                   (compareTo.Id != null && !compareTo.Id.Equals(default(IdT))) &&
                   Id.Equals(compareTo.Id);
        }

        private IdT id = default(IdT);
    }
}